package com.hmall.search.service.impl;

import cn.hutool.core.util.StrUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.Suggestion;
import co.elastic.clients.json.JsonData;
import com.hmall.api.client.ItemClient;
import com.hmall.api.dto.ItemDTO;
import com.hmall.common.utils.BeanUtils;
import com.hmall.search.domain.po.ItemDoc;
import com.hmall.search.domain.query.ItemPageQuery;
import com.hmall.search.domain.vo.PageVO;
import com.hmall.search.service.ISearchService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@RequiredArgsConstructor
public class SearchServiceImpl implements ISearchService {

    private final ElasticsearchClient esClient;
    //索引名 items
    private static final String INDEX_NAME = "items";

    private final ItemClient itemClient;

    @Override
    public PageVO<ItemDoc> search(ItemPageQuery itemPageQuery) {
        //返回结果
        PageVO<ItemDoc> result = PageVO.empty(0L, 0L);

        try {
            //1、创建查询请求构建对象
            SearchRequest.Builder builder = new SearchRequest.Builder();
            //搜索，过滤--》使用复合查询
            BoolQuery.Builder boolQuery = QueryBuilders.bool();
            //设置搜索关键字
            boolean isNeedHighlight = false;
            if (StrUtil.isNotBlank(itemPageQuery.getKey())) {
                boolQuery.must(QueryBuilders.match(m -> m.field("name").query(itemPageQuery.getKey())));
                isNeedHighlight = true;
            }
            //品牌过滤
            if (StrUtil.isNotBlank(itemPageQuery.getBrand())) {
                boolQuery.filter(QueryBuilders.term(t -> t.field("brand").value(itemPageQuery.getBrand())));
            }
            //分类过滤
            if (StrUtil.isNotBlank(itemPageQuery.getCategory())) {
                boolQuery.filter(QueryBuilders.term(t -> t.field("category").value(itemPageQuery.getCategory())));
            }
            //价格过滤大
            if (itemPageQuery.getMaxPrice() != null) {
                boolQuery.filter(QueryBuilders.range(r -> r.field("price").lte(JsonData.of(itemPageQuery.getMaxPrice()))));
            }
            //价格过滤小
            if (itemPageQuery.getMinPrice() != null) {
                boolQuery.filter(QueryBuilders.range(r -> r.field("price").gte(JsonData.of(itemPageQuery.getMinPrice()))));
            }
            //向请求对象中设置查询对象
            builder.query(q -> q.bool(boolQuery.build()));
            //设置分页
            builder.from(itemPageQuery.from());
            builder.size(itemPageQuery.getPageSize());

            //排序
            if (StrUtil.isNotBlank(itemPageQuery.getSortBy())) {
                builder.sort(s -> s.field(f -> f.field(itemPageQuery.getSortBy())
                        .order(itemPageQuery.getIsAsc() ? SortOrder.Asc : SortOrder.Desc)
                ));
            } else {
                //如果没有传递排序的字段；那么按照更新的时间排序
                builder.sort(s -> s.field(f -> f.field("updateTime").order(SortOrder.Desc)));
            }
            //高亮
            if (isNeedHighlight) {
                builder.highlight(h -> h.fields("name", f -> f.preTags("<em>").postTags("</em>")));
            }
            //搜索获得搜索结果
            SearchRequest searchRequest = builder.index(INDEX_NAME).build();
            SearchResponse<ItemDoc> searchResponse = esClient.search(searchRequest, ItemDoc.class);
            //总记录数
            long total = searchResponse.hits().total().value();
            result.setTotal(total);
            //根据总记录数和页大小计算总页数
            long pages = total % itemPageQuery.getPageSize() == 0 ? total / itemPageQuery.getPageSize() : total / itemPageQuery.getPageSize() + 1;
            result.setPages(pages);

            //当前页商品列表
            List<Hit<ItemDoc>> hits = searchResponse.hits().hits();
            List<ItemDoc> itemDocList = new ArrayList<>(hits.size());
            if (hits != null && hits.size() > 0) {
                for (Hit<ItemDoc> hit : hits) {
                    ItemDoc itemDoc = hit.source();
                    itemDocList.add(itemDoc);
                    if (isNeedHighlight) {
                        if (hit.highlight() != null && hit.highlight().containsKey("name")) {
                            //获取高亮标题集合
                            List<String> StringList = hit.highlight().get("name");
                            itemDoc.setName(StringList.get(0));
                        }
                    }
                }
            }
            //设置最终的返回数据到result
            result.setList(itemDocList);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return result;
    }

    @Override
    public Map<String, List<String>> filter(ItemPageQuery itemPageQuery) {
        try {
            //只有当分类或品牌没有选择的时才有必要去查对应的数据
            if (StrUtil.isBlank(itemPageQuery.getCategory()) || StrUtil.isBlank(itemPageQuery.getBrand())) {
                Map<String, List<String>> resultMap = new HashMap<>();
                //1、创建查询请求构建对象
                SearchRequest.Builder searchRequestBuilder = new SearchRequest.Builder().index(INDEX_NAME);
                //2、设置参数
                //是否需要查询分类聚合数据
                boolean isNeedCategory = true;
                //是否需要查询品牌聚合数据
                boolean isNeedBrand = true;
                //设置搜索关键字及过滤条件
                BoolQuery.Builder boolQuery = QueryBuilders.bool();
                if (StrUtil.isNotBlank(itemPageQuery.getKey())) {
                    boolQuery.must(m -> m.match(builder -> builder.field("name").query(itemPageQuery.getKey())));
                }
                //设置分类过滤查询
                if (StrUtil.isNotBlank(itemPageQuery.getCategory())) {
                    boolQuery.filter(f -> f.term(m -> m.field("category").value(itemPageQuery.getCategory())));
                    isNeedCategory = false;
                }
                //设置品牌过滤查询
                if (StrUtil.isNotBlank(itemPageQuery.getBrand())) {
                    boolQuery.filter(f -> f.term(m -> m.field("brand").value(itemPageQuery.getBrand())));
                    isNeedBrand = false;
                }

                //设置价格过滤查询
                if (itemPageQuery.getMinPrice() != null) {
                    boolQuery.filter(f -> f.range(r -> r.field("price").gte(JsonData.of(itemPageQuery.getMinPrice()))));
                }
                if (itemPageQuery.getMaxPrice() != null) {
                    boolQuery.filter(f -> f.range(r -> r.field("price").lte(JsonData.of(itemPageQuery.getMaxPrice()))));
                }
                searchRequestBuilder.query(q -> q.bool(boolQuery.build()));

                //设置不返回文档
                searchRequestBuilder.size(0);

                //设置分类聚合查询
                if (isNeedCategory) {
                    searchRequestBuilder.aggregations("category_agg", a -> a.terms(t -> t.field("category").size(20)));
                }
                //设置品牌聚合查询
                if (isNeedBrand) {
                    searchRequestBuilder.aggregations("brand_agg", a -> a.terms(t -> t.field("brand").size(20)));
                }

                //3、执行请求
                SearchResponse<ItemDoc> searchResponse = esClient.search(searchRequestBuilder.build(), ItemDoc.class);
                //4、解析结果
                //解析分类聚合数据
                if (isNeedCategory) {
                    if (searchResponse.aggregations().containsKey("category_agg")) {
                        Aggregate categoryAgg = searchResponse.aggregations().get("category_agg");
                        categoryAgg.sterms().buckets().array().forEach(b -> {
                            String category = b.key().stringValue();
                            resultMap.computeIfAbsent("category", k -> new ArrayList<>()).add(category);
                        });
                    }
                }

                //解析品牌聚合数据
                if (isNeedBrand) {
                    if (searchResponse.aggregations().containsKey("brand_agg")) {
                        Aggregate brandAgg = searchResponse.aggregations().get("brand_agg");
                        brandAgg.sterms().buckets().array().forEach(b -> {
                            String brand = b.key().stringValue();
                            resultMap.computeIfAbsent("brand", k -> new ArrayList<>()).add(brand);
                        });
                    }
                }

                return resultMap;
            }
        } catch (Exception e) {
            throw new RuntimeException("聚合查询es中商品数据失败", e);
        }
        return Map.of();
    }


    @Override
    public List<String> suggestion(String key) {
        List<String> resultList = new ArrayList<>();
        try {
            if (StrUtil.isNotBlank(key)) {
                //1、创建查询请求构建对象
                SearchRequest.Builder searchRequestBuilder = new SearchRequest.Builder().index(INDEX_NAME);
                //2、设置参数
                searchRequestBuilder.suggest(s -> s.suggesters("item-suggest", suggest -> suggest
                        .completion(c -> c.field("suggestion").skipDuplicates(true).size(10))
                        .text(key)
                ));
                //3、执行请求
                SearchResponse<ItemDoc> searchResponse = esClient.search(searchRequestBuilder.build(), ItemDoc.class);
                //4、解析结果
                List<Suggestion<ItemDoc>> suggestionList = searchResponse.suggest().get("item-suggest");
                if (suggestionList != null && !suggestionList.isEmpty()) {
                    for (Suggestion<ItemDoc> itemDocSuggestion : suggestionList) {
                        itemDocSuggestion.completion().options()
                                .forEach(opt -> {
                                    resultList.add(opt.text());
                                });
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("自动补全查询异常" + e);
        }
        return resultList;
    }

    @Override
    public void saveItemById(Long itemId) {
        try {
            //根据商品id查询商品
            ItemDTO itemDTO = itemClient.queryItemById(itemId);
            ItemDoc itemDoc = BeanUtils.copyBean(itemDTO, ItemDoc.class);
            itemDoc.setSuggestion(List.of(itemDoc.getCategory(), itemDoc.getBrand()));

            esClient.index(i -> i.index(INDEX_NAME).id(itemId.toString()).document(itemDoc));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void deleteItemById(Long itemId) {
        try {
            esClient.delete(d -> d.index(INDEX_NAME).id(itemId.toString()));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}